package ao.util.io;

import ao.util.misc.CloseableIterable;
import ao.util.misc.CloseableIterator;
import ao.util.misc.Exceptions;
import org.apache.log4j.Logger;

import java.io.*;

/**
 * Date: Oct 17, 2008
 * Time: 3:13:25 PM
 */
public class Slurpy
{
    //-------------------------------------------------------------------------
    private static final Logger LOG =
            Logger.getLogger( Slurpy.class );


    //-------------------------------------------------------------------------
    private Slurpy() {}


    //-------------------------------------------------------------------------
    public static boolean write(
            String contents, String fileName)
    {
        return write( contents, new File( fileName ) );
    }

    public static boolean write(
            String contents, File outFile)
    {
        try
        {
            boolean closeFailed[] = {false};
            doWrite(contents, outFile, closeFailed);
            return !closeFailed[0];
        }
        catch (IOException e)
        {
            LOG.warn("Unable to write", e);
            return false;
        }
    }

    private static void doWrite(
            String  contents,
            File    outFile,
            boolean closeFailed[])
                throws IOException
    {
        Dirs.pathTo( outFile );

        OutputStream out = null;
        try
        {
            out = new FileOutputStream( outFile );
            out.write( contents.getBytes() );
        }
        finally
        {
            if (out != null)
            {
                try
                {
                    out.close();
                }
                catch (IOException e)
                {
                    closeFailed[0] = true;
                    LOG.warn("Unable to close file", e);
                }
            }
        }
    }


    //-------------------------------------------------------------------------
    public static String read(String contentsOf)
    {
        return read( new File( contentsOf ) );
    }
    public static String read(File contentsOf)
    {
        if (! contentsOf.canRead()) return null;

        InputStream fis = null;
        try
        {
            fis = new BufferedInputStream(
                    new FileInputStream(contentsOf));
            return doSlurpString( contentsOf, fis );
        }
        catch (IOException e)
        {
            throw new Error( e );
        }
        finally
        {
            if (fis != null)
            {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static String doSlurpString(
            File        contentsOf,
            InputStream fis) throws IOException
    {
        StringBuilder str = new StringBuilder();

        for (int i = (int)(contentsOf.length() - 1);
                 i >= 0;
                 i--) {
            str.append((char) fis.read());
        }

        return str.toString();
    }


    //-------------------------------------------------------------------------
    public static CloseableIterable<String> readLines(String file)
    {
        return readLines(new File(file));
    }

    public static CloseableIterable<String> readLines(
            final File file)
    {
        try {
            return doReadLines( file );
        } catch (FileNotFoundException e) {
            throw Exceptions.wrap( e );
        }
    }

    private static CloseableIterable<String> doReadLines(
            final File file) throws FileNotFoundException
    {
        if (file == null || ! file.canRead()) {
            return null;
        }

        return CloseableIterable.Impl.fromIterator(
                new CloseableIterator<String>() {
                    private final BufferedReader reader =
                            new BufferedReader(
                                    new FileReader( file ));

                    private String nextLine;

                    @Override
                    public synchronized void close()
                    {
                        try {
                            reader.close();
                        } catch (IOException e) {
                            throw Exceptions.wrap( e );
                        }
                    }

                    @Override
                    public synchronized boolean hasNext()
                    {
                        if (nextLine != null) {
                            return true;
                        }

                        try {
                            nextLine = reader.readLine();
                        } catch (IOException e) {
                            throw Exceptions.wrap( e );
                        }

                        if (nextLine == null) {
                            close();
                        }
                        return (nextLine != null);
                    }

                    @Override
                    public synchronized String next()
                    {
                        hasNext();
                        return nextLine;
                    }

                    @Override
                    public synchronized void remove() {
                        throw new UnsupportedOperationException();
                    }
                });
    }
}
